home *** CD-ROM | disk | FTP | other *** search
/ SuperHack / SuperHack CD.bin / CODING / GRAPHICS / VOXELTUT.ZIP / VOXEL.C next >
Encoding:
C/C++ Source or Header  |  1996-05-06  |  16.2 KB  |  549 lines

  1. //                                                  
  2. //      Heightfield Engine       
  3. //      written by: Alex Chalfin
  4. //      email: achalfin@uceng.uc.edu                
  5. //
  6. //      Tested with Watcom C/C++ v10.0 and v10.5
  7.  
  8. #include <stdio.h>
  9. #include <stdlib.h>
  10. #include <conio.h>
  11. #include <string.h>
  12. #include <malloc.h>
  13. #include <math.h>
  14. #include <time.h>
  15. #include <i86.h>
  16.  
  17. // the MAPSHIFT constant controls the size of the map. The map dimentions
  18. // are (2^MAPSHIFT)x(2^MAPSHIFT). So if you set MAPSHIFT equal to 8, you
  19. // will get a 256x256 map.
  20. #define  MAPSHIFT    8              // 2^MAPSHIFT = size of map
  21.  
  22. // Macros dependant on MAPSHIFT. They change with the size of the map. They
  23. // should not be changed manually. 
  24. #define  MAPSIZE     (1<<MAPSHIFT)           // size of one side of the map
  25. #define  BYTE(a)     (a & (MAPSIZE-1))       // put the range within MAPSIZE
  26. #define  INDEX(x,y)  ((BYTE(y) << MAPSHIFT) + BYTE(x)) // index calculation 
  27. #define  fINDEX(x,y) (INDEX(x >> 8, y >> 8)) // fixed point index calculation
  28.  
  29. // A couple of miscelanous constants
  30. #define  NUMSMOOTH   3     // smooth the map 3 times
  31. #define  FARDEPTH    40    // number of depth steps
  32. #define  NEARDEPTH   0     // do not change this number....
  33. #define  NUMSTEPS    FARDEPTH-NEARDEPTH
  34. #define  HEIGHTMUL   16    // perspective multiplication factor
  35. #define  SLOPE       2     // do not change this number....
  36. #define  RENDERWIDTH 320   // vertical scanlines to render
  37.  
  38. #define  RANDOM(a)   (rand() / (RAND_MAX / (a))) // a simple random function
  39.  
  40. typedef struct tview    // a "view" on the voxel map
  41. {                       // uses 3 points to define a view
  42.   int x1, y1, x2, y2;   // a center or viewer position and a point to the
  43.   int cx, cy;           // left and a point to the right. The side points are
  44.   int z;
  45. } tview;                // used to define the viewer rotation and where to
  46.                         // trace on the height map
  47.  
  48. typedef struct hrec     // struct to store a height and a color for a vertical
  49. {                       // line for the rendering process.
  50.     int y;
  51.     int c;
  52. } hrec;
  53.  
  54. typedef struct               // 4 byte division for union use
  55. {
  56.     char b1, b2, b3, b4;
  57. } _bytes;
  58.  
  59. typedef union register32_8  // simulate a byte accessable 32-bit register
  60. {
  61.     int   dword;
  62.      _bytes bytes; 
  63. } register32_8;
  64.  
  65. char *heightmap;          // height feild for the voxels
  66. char *colormap;           // colors for the voxels
  67. char *vpage;              // a virtual screen page
  68. hrec *buf1;               // buffer for storing the "top" part
  69. hrec *buf2;               // buffer for storing the "bottom" part
  70. int   divtable[256];      // a 256/x table
  71. int   sine[256];          // sin lookup table
  72. int   cosine[256];        // cos lookup table
  73.  
  74. void createmaps();
  75. void createfractalmap(int x1, int y1, int x2, int y2);
  76. char newcolor(int mc, int n, int dvd);
  77. void cleanup();
  78. void smoothmap();
  79. void makecolormap();
  80. void buildtables();
  81. void drawscreen(tview *v);
  82. void gouraudbuffers();
  83. void makepalette();
  84. void setrgb(char n, char r, char g, char b);
  85. void setview(int speed, int viewangle, int moveangle, int viewer, tview *v);
  86. short int initmouse(); 
  87. void readmickey(short int *x, short int *y);
  88.  
  89.  
  90. void vmode(short int);
  91. // video mode initialization routine. Use to init mode 13h and mode 03h
  92. #pragma aux vmode = \
  93.     "int 10h"       \
  94.     parm [ax]       \
  95.     modify [ax];
  96.  
  97. void vpagecopy(void *src);
  98. // virtual page copy and clear
  99. #pragma aux vpagecopy =   \
  100.   "Mov  edx,esi"          \
  101.   "Mov  edi,0a0000h"      \
  102.   "Mov  ecx,16000"        \
  103.   "Mov  eax,ecx"          \
  104.   "Rep  Movsd"            \
  105.   "Mov  ecx,eax"          \
  106.   "Xor  eax,eax"          \
  107.   "Mov  edi,edx"          \
  108.   "Rep  Stosd"            \
  109.   parm [esi]              \
  110.   modify [ecx edx edi eax];
  111.  
  112. main()
  113. {
  114.     tview mainview;  // current view
  115.     int st, et;      // time counters for FPS calculations
  116.     int frame;       // frame counter for FPS calculations
  117.     int actionflag;  // mouse available flag 
  118.     short int x;     // mouse mickey "x" counter
  119.     short int y;     // mouse mickey "y" counter
  120.  
  121.  
  122.     createmaps();    // build the terrain and color maps
  123.     buildtables();   // build sin/cos and div tables
  124.     vmode(0x13);     // set the video mode
  125.     makepalette();   // set nice read fade palette
  126.     frame = 0;
  127.     st = clock();    // starting time counter
  128.  
  129.     // initialize view
  130.     setview(0, 0, 0, heightmap[fINDEX(mainview.cx, mainview.cy)], &mainview);
  131.  
  132.     actionflag = initmouse();  // set flag if a mouse is available
  133.  
  134.     while (inp(0x60) != 1)
  135.     {
  136.       if (actionflag == 0)
  137.         // automate movement because mouse isn't availabe
  138.         setview(256, 0, 0, heightmap[fINDEX(mainview.cx, mainview.cy)], &mainview);
  139.       else
  140.       {
  141.         // set view based on mouse movement
  142.         readmicky(&x, &y);
  143.         mainview.cx += -y*4;
  144.         mainview.x1 += -y*4;
  145.         mainview.x2 += -y*4;
  146.         mainview.cy += x*4;
  147.         mainview.y1 += x*4;
  148.         mainview.y2 += x*4;
  149.         mainview.z = heightmap[fINDEX(mainview.cx, mainview.cy)];
  150.       }
  151.       drawscreen(&mainview);  // render the image to the virtual screen
  152.       vpagecopy(vpage);       // copy the virtual screen to video memory
  153.       frame++;
  154.     }
  155.     et = clock();
  156.     vmode(0x03);
  157.     printf("%5.2f frames per second\n\n", ((float)(frame*CLOCKS_PER_SEC))/(et-st));
  158.     cleanup();
  159.     printf("Voxel code by Alex Chalfin\n");
  160.     printf("email: achalfin@uceng.uc.edu\n");
  161. }
  162.  
  163. void setview(int speed, int viewangle, int moveangle, int viewer, tview *v)
  164. // sets the viewpoint parameters
  165. // in:  speed - the speed you are traveling in 24.8 fixed point
  166. //      viewangle - the angle to look. range 0..255
  167. //      moveangle - the angle to move. range 0..255
  168. //      viewer - height of the viewer
  169. //      v - tview to set parameters of
  170. {
  171.   // update the center position based on speed and move angle
  172.   moveangle &= 255;
  173.   v->cx += ((speed*cosine[moveangle]) >> 8);
  174.   v->cy += ((speed*sine[moveangle]) >> 8);
  175.  
  176.   // set viewing angle
  177.   viewangle &= 255;
  178.   v->x1 = (FARDEPTH * cosine[viewangle]) + (SLOPE*FARDEPTH * sine[viewangle]) + v->cx;
  179.   v->y1 = (FARDEPTH * sine[viewangle])   - (SLOPE*FARDEPTH * cosine[viewangle]) + v->cy;
  180.  
  181.   v->x2 = (FARDEPTH * cosine[viewangle]) - (SLOPE*FARDEPTH * sine[viewangle]) + v->cx;
  182.   v->y2 = (FARDEPTH * sine[viewangle])   + (SLOPE*FARDEPTH * cosine[viewangle]) + v->cy;
  183.  
  184.   v->z = viewer;
  185. }
  186.  
  187. void drawscreen(tview *v)
  188. // This is the main drawing routine.
  189. // It takes a TVIEW structure as a parameter and renders the scene for you.
  190. // It will render at any rotation (depends on the TVIEW passed in)
  191. // it does not do view tilting (easily added).
  192. // Based on 24.8 fixed point and does proper map reapeating when necessary
  193. {
  194.     int x,y,i;                             // general counters
  195.     int xstep, xval, ystep, yval;          // constant z interpolation vars
  196.     int viewer, per;                       // perspective vars
  197.     int lxval1, lxval2, lyval1, lyval2;    // map tracing vars
  198.     int lxstep1, lxstep2, lystep1, lystep2;
  199.     static int leest[RENDERWIDTH];         // interpolation index list
  200.     int index, oldi;                       // map location and old map location
  201.     hrec *tbuf;                            // buf record. for pointer walks
  202.  
  203.     viewer = v->z + 20;   // set appropriate viewing position
  204.  
  205.     // set up map traversing vars
  206.     lxval1 = v->x1;
  207.     lxval2 = v->x2;
  208.     lxstep1 = (v->cx - v->x1) / NUMSTEPS;
  209.     lxstep2 = (v->cx - v->x2) / NUMSTEPS;
  210.  
  211.     lyval1 = v->y1;
  212.     lyval2 = v->y2;
  213.     lystep1 = (v->cy - v->y1) / NUMSTEPS;
  214.     lystep2 = (v->cy - v->y2) / NUMSTEPS;
  215.  
  216.  
  217.     // do the first line of the rendering. i.e. furthest from the viewer
  218.     xval = lxval1;
  219.     xstep = (lxval2 - lxval1) / RENDERWIDTH;
  220.     yval = lyval1;
  221.     ystep = (lyval2 - lyval1) / RENDERWIDTH;
  222.     tbuf = buf1;
  223.     per = (HEIGHTMUL << 8) / NUMSTEPS;
  224.  
  225.     for (x = 0; x < RENDERWIDTH; x++)
  226.     {
  227.         i = fINDEX(xval, yval);
  228.         tbuf->y = 99 - (((heightmap[i] - viewer)*per) >> 8);
  229.         tbuf->c = colormap[i];
  230.         xval += xstep;
  231.         yval += ystep;
  232.         tbuf++;
  233.     }
  234.  
  235.     lxval1 += lxstep1;
  236.     lxval2 += lxstep2;
  237.     lyval1 += lystep1;
  238.     lyval2 += lystep2;
  239.  
  240.   // do the intermediate lines
  241.  
  242.     for (y = (FARDEPTH - 1); y > NEARDEPTH; y--)
  243.     {
  244.         xval = lxval1;
  245.         xstep = (lxval2 - lxval1)/ RENDERWIDTH;
  246.         yval = lyval1;
  247.         ystep = (lyval2 - lyval1)/ RENDERWIDTH;
  248.         tbuf = buf2;
  249.         index = 0;  // initialize interpolation index
  250.         oldi = -1;
  251.         per = (HEIGHTMUL << 8) / (y - NEARDEPTH);  // set perspective value
  252.  
  253.         for (x = 0; x < RENDERWIDTH; x++)
  254.         {
  255.           i = fINDEX(xval, yval);
  256.           if ((oldi != i) || (x == (RENDERWIDTH-1)))
  257.           {
  258.             tbuf->y = 99 - (((heightmap[i] - viewer)*per) >> 8);
  259.             tbuf->c = colormap[i];
  260.             leest[index++] = x;
  261.             oldi = i;
  262.           }
  263.           xval += xstep;
  264.           yval += ystep;
  265.           tbuf++;
  266.         }
  267.  
  268. // this is where the main smoothing occurs. The height and color are
  269. // interpolated for maximum effect. Interpolation is done in screen space
  270. // for maximum speed.
  271.    
  272.         for (x = 0; x <= (index - 2); x++)
  273.         {
  274.           xval = buf2[leest[x]].y << 8;
  275.           yval = buf2[leest[x]].c << 8;
  276.           per = divtable[leest[x+1]-leest[x]];
  277.           xstep = (buf2[leest[x+1]].y - buf2[leest[x]].y) * per;
  278.           ystep = (buf2[leest[x+1]].c - buf2[leest[x]].c) * per;
  279.           tbuf = &buf2[leest[x]+1];
  280.  
  281.           for (i = leest[x]+1; i < leest[x+1]; i++)
  282.           {
  283.              xval+=xstep;
  284.              yval+=ystep;
  285.              tbuf->y = xval >> 8;
  286.              tbuf->c = yval >> 8;
  287.              tbuf++;
  288.           }
  289.         }
  290.  
  291. // draw the vertical bars
  292.         verticallines();
  293.  
  294. // swap the buffers
  295.         tbuf = buf1;
  296.         buf1 = buf2;
  297.         buf2 = tbuf;
  298.  
  299.         lxval1 += lxstep1;
  300.         lxval2 += lxstep2;
  301.         lyval1 += lystep1;
  302.         lyval2 += lystep2;
  303.     }
  304.  
  305.    // do the closest row
  306.  
  307.    xval = lxval1;
  308.    xstep = (lxval2 - lxval1) / RENDERWIDTH;
  309.    yval = lyval1;
  310.    ystep = (lyval2 - lyval1) / RENDERWIDTH;
  311.    tbuf = buf2;
  312.    for (x = 0; x < RENDERWIDTH; x++)
  313.    {
  314.        tbuf->y = 199;  // set to bottom of the screen so no gaps appear
  315.        tbuf->c = colormap[fINDEX(xval, yval)];
  316.        xval += xstep;
  317.        yval += ystep;
  318.        tbuf++;
  319.    }
  320.    verticallines();
  321.     
  322. }
  323.  
  324. void verticallines()
  325. // draws the vertical lines between two "buffers"
  326. {
  327.     int x, y;
  328.     int cstep;
  329.     int y1, y2;
  330.     register32_8 cval;
  331.  
  332.     for (x = 0; x < RENDERWIDTH; x++)
  333.     {
  334.         if (buf1[x].y < buf2[x].y)
  335.         {
  336.             y1 = buf1[x].y;
  337.             y2 = buf2[x].y;
  338.             cval.dword = buf1[x].c << 8;
  339.             if ((y2-y1) > 200)  // check for divtable over-run
  340.               cstep = ((buf2[x].c - buf1[x].c) << 8) / (y2-y1);
  341.             else
  342.               cstep = ((buf2[x].c - buf1[x].c) * divtable[y2-y1]);
  343.             if (y2 > 199) y2 = 199;
  344.             if (y1 < 0)
  345.             {
  346.               cval.dword += cstep*(-y1);
  347.               y1 = 0;
  348.             }
  349.             y1 = ( (int)(vpage) + (y1 << 8) + (y1 << 6) + x);
  350.             y2 = ( (int)(vpage) + (y2 << 8) + (y2 << 6) + x);
  351.             for (y = y1; y <= y2; y+=320)
  352.             {
  353.                 *(char *)(y) = cval.bytes.b2;
  354.                 cval.dword+=cstep;
  355.             }
  356.         }
  357.     }
  358. }
  359.  
  360.  
  361.  
  362. void makepalette()
  363. {
  364.    int i;
  365.  
  366. // For a red landscape
  367.     for (i = 1; i < 64; i++)
  368.       setrgb(i, 16 + (i/4), i/8, 0);
  369.     for (i = 64; i < 128; i++)
  370.       setrgb(i, 32 + ((i-64)/2), i/8, 0);
  371.  
  372. // For a green Landscape
  373. //    for (i = 1; i < 64; i++)
  374. //      setrgb(i, i/8, 16 + (i/4), 0);
  375. //    for (i = 64; i < 128; i++)
  376. //      setrgb(i, i/8, 32 + ((i-64)/2), 0);
  377. }
  378.  
  379. void buildtables()
  380. {
  381.   int i;
  382.  
  383.   for (i = 1; i < 256; i++)
  384.     divtable[i] = 256/i;
  385.   for (i = 0; i < 256; i++)
  386.   {
  387.     sine[i] = (sin(i*2*3.141592/256.0)*256);
  388.     cosine[i] = (cos(i*2*3.141592/256.0)*256);
  389.   }
  390. }
  391.  
  392.  
  393. void createmaps()
  394. {
  395.     int i;
  396.     srand(MAPSHIFT);
  397.     // allocate necessary memory
  398.     heightmap = (char *)malloc(MAPSIZE*MAPSIZE);
  399.     colormap = (char *)malloc(MAPSIZE*MAPSIZE);
  400.     vpage = (char *)malloc(64000);
  401.     buf1 = (hrec *)malloc(RENDERWIDTH * sizeof(hrec));
  402.     buf2 = (hrec *)malloc(RENDERWIDTH * sizeof(hrec));
  403.  
  404.     // clear all memory
  405.     memset(heightmap, 0, MAPSIZE*MAPSIZE);
  406.     memset(vpage, 0, 64000);
  407.  
  408.     printf("Creating %dx%d fractal terrain\n", MAPSIZE, MAPSIZE);
  409.     heightmap[0] = 64;   // initialize starting point on map
  410.     createfractalmap(0, 0, MAPSIZE, MAPSIZE);
  411.  
  412.     printf("Smooting terrain\n");
  413.     for (i = 0; i < NUMSMOOTH; i++)
  414.       smoothmap();
  415.  
  416.     makecolormap();  
  417. }
  418.  
  419. void cleanup()
  420. // free allocated memory
  421. {
  422.     free(heightmap);
  423.     free(colormap);
  424.     free(vpage);
  425.     free(buf1);
  426.     free(buf2);
  427. }
  428.  
  429. void createfractalmap(int x1, int y1, int x2, int y2)
  430. // recursive fractal terrain builder 
  431. {
  432.     int p1, p2, p3, p4;
  433.     int xn, yn, dxy;
  434.  
  435.     if (((x2-x1) < 2) && ((y2-y1) < 2))  // make sure their is something to do
  436.       return;
  437.  
  438.     p1 = heightmap[INDEX(x1,y1)];
  439.     p2 = heightmap[INDEX(x1,y2)];
  440.     p3 = heightmap[INDEX(x2,y1)];
  441.     p4 = heightmap[INDEX(x2,y2)];
  442.  
  443.     xn = (x2+x1) >> 1;
  444.     yn = (y2+y1) >> 1;
  445.     dxy = 5*(x2 - x1 + y2 - y1) / 3;
  446.  
  447.     if (heightmap[INDEX(xn,y1)] == 0)
  448.       heightmap[INDEX(xn,y1)] = newcolor(p1+p3, dxy, 2);
  449.     if (heightmap[INDEX(x1,yn)] == 0)
  450.       heightmap[INDEX(x1,yn)] = newcolor(p2+p4, dxy, 2);
  451.     if (heightmap[INDEX(x2,yn)] == 0)
  452.       heightmap[INDEX(x2,yn)] = newcolor(p3+p4, dxy, 2);
  453.     if (heightmap[INDEX(xn,y2)] == 0)
  454.       heightmap[INDEX(xn,y2)] = newcolor(p1+p2, dxy, 2);
  455.     heightmap[INDEX(xn,yn)] = newcolor(p1+p2+p3+p4, dxy, 4);
  456.     createfractalmap(x1, y1, xn, yn);
  457.     createfractalmap(xn, y1, x2, yn);
  458.     createfractalmap(x1, yn, xn, y2);
  459.     createfractalmap(xn, yn, x2, y2);
  460. }
  461.  
  462. char newcolor(int mc, int n, int dvd)
  463. {
  464.     int loc;
  465.  
  466.     loc = (mc + n - RANDOM(n << 1)) / dvd - 1;
  467.     if (loc > 255)
  468.       loc = 255;
  469.     if (loc < 10)
  470.       loc = 10;
  471.     return(loc);
  472. }
  473.  
  474. void smoothmap()
  475. // smooths the map. Gives better appearence
  476. {
  477.     int x,y;
  478.  
  479.     for (x = 0; x < MAPSIZE; x++)
  480.       for (y = 0; y < MAPSIZE; y++)
  481.         heightmap[INDEX(x,y)] = (heightmap[INDEX(x-1,y-1)] +
  482.                                  heightmap[INDEX(x-1,y+1)] +
  483.                                  heightmap[INDEX(x+1,y-1)] +
  484.                                  heightmap[INDEX(x+1,y+1)] +
  485.                                  heightmap[INDEX(x,  y-1)] +
  486.                                  heightmap[INDEX(x,  y+1)] +
  487.                                  heightmap[INDEX(x-1,y)] +
  488.                                  heightmap[INDEX(x+1,y)]) >> 3;
  489. }
  490.  
  491. void makecolormap()
  492. // builds a color map based on the height map
  493. // attempts to add some color shift
  494. {
  495.     int x,y,temp;
  496.  
  497.     for (x = 0; x < MAPSIZE; x++)
  498.      for (y = 0; y < MAPSIZE; y++)
  499.      {
  500.          temp = heightmap[INDEX(x,y)] >> 1;
  501.          if (heightmap[INDEX(x,y-1)] < heightmap[INDEX(x,y)])
  502.          {
  503.             temp -= 5*(heightmap[INDEX(x,y)] - heightmap[INDEX(x, y-1)]);
  504.             if (temp < 0)
  505.                temp = 0;
  506.          }
  507.          else
  508.          {
  509.              if (heightmap[INDEX(x,y-1)] > heightmap[INDEX(x,y)])
  510.              {
  511.                  temp += 3*(heightmap[INDEX(x,y-1)] - heightmap[INDEX(x,y)]);
  512.                  if (temp > 127)
  513.                    temp = 127;
  514.              }
  515.          }
  516.          colormap[INDEX(x,y)] = temp;    
  517.      }
  518. }
  519.  
  520. void setrgb(char n, char r, char g, char b)
  521. {
  522.    outp(0x03c8, n);
  523.    outp(0x03c9, r);
  524.    outp(0x03c9, g);
  525.    outp(0x03c9, b);
  526. }
  527.  
  528. short int initmouse()
  529. // checks for mouse driver.
  530. // returns:  0 - mouse not installed
  531. //           -1 - mouse installed
  532. {
  533.   union REGS inregs, outregs;
  534.   inregs.w.ax = 0;
  535.   int386(0x33, &inregs, &outregs);
  536.   return(outregs.w.ax);
  537. }
  538.  
  539. void readmicky(short int *x, short int *y)
  540. // returns the mikey count for mouse movement
  541. {
  542.   union REGS inregs, outregs;
  543.   inregs.w.ax = 0x0b;
  544.   int386(0x33, &inregs, &outregs);
  545.   *x = outregs.w.cx;
  546.   *y = outregs.w.dx;
  547. }
  548.  
  549.